home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The X-Philes (2nd Revision)
/
The X-Philes Number 1 (1995).iso
/
xphiles
/
hp48hor2
/
tasc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-18
|
12KB
|
404 lines
/* Tasc: HP48-binary <-> HP48-ASC.
Copyright 1992 by Jonathan T. Higa.
Distribute freely.
You may make modifications to suit your needs; simply document the changes.
Dec 1991 to Mar 1992: parses hp48 binary correctly,
determines type of source file, handles stdin & stdout
12 June 1992: v2.50 -- auto name completion
19 June 1992: v2.51 -- bug fix for names < 4 chars; added @ comments
22 June 1992: v2.52 -- generalized report syntax
*/
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ATOB 1
#define BTOA 2
typedef unsigned long ulong;
const char ASCSUF[]=".asc"; /* ASC file suffix */
const char VERSION[]="2.52"; /* TASC version */
const char *ROMFMT="ROM Revision: %c\n";
/* ROM Revision report format: use %c */
const char *BYTESFMT="BYTES: #%lXh %ld%s\n";
const char *HALFFMT=".5";
/* BYTES report: use long for checksum, long for integral bytes,
and string for half-byte format string */
int verb=1; /* verbosity flag */
char rom='E'; /* default rom revision letter */
void *alloc(size_t n);
int stricmp(const char *s1, const char *s2);
ulong popbitq(ulong *buf, int *bsize, int nbits);
ulong pushbitq(ulong *buf, int *bsize, int nbits, ulong bits);
int bintoasc(FILE *fbin, FILE *fasc);
int translate(const char *fsrc, const char *fdest, int mode);
#define calc_crc(crc, hex) (crc=(crc>>4)^(((crc^(hex))&0xF)*0x1081))
/* Recalculates the Cyclic Redundancy Check value based on the old CRC
value crc and a new nibble hex.
Note: crc should be an unsigned lvalue initialized to 0. */
#define lowbits(n) ((1uL << (n)) - 1uL)
/* Creates an unsigned long with only the low n bits set. */
void *alloc(size_t n)
/* Safe memory allocation.
Exits on error. */
{
void *p;
if (n <= 0) {
fputs("mem: Invalid block size\n", stderr);
exit(1);
}
p = malloc(n);
if (!p) {
perror("mem");
exit(1);
}
return p;
}
int stricmp(const char *s1, const char *s2)
/* Case-insensitive string comparison via conversion to upper case.
Return -1 if s1 is alphabetically before s2, 1 if after, 0 if equal. */
{
int i, c1, c2;
i = 0;
do {
c1 = toupper(s1[i]);
c2 = toupper(s2[i]);
i++;
if (c1 < c2) return -1;
if (c1 > c2) return 1;
} while (c1);
return 0;
}
ulong popbitq(ulong *buf, int *bsize, int nbits)
/* Returns the lowest nbits bits of *buf.
Removes those bits from *buf and adjusts *bsize appropriately. */
{
ulong b;
b = *buf & lowbits(nbits);
*buf >>= nbits;
if ((*bsize -= nbits) < 0) {
fputs("tasc: Bit buffer underflow\n", stderr);
exit(1);
}
return b;
}
ulong pushbitq(ulong *buf, int *bsize, int nbits, ulong bits)
/* Pushes the low nbits of bits onto the high end of *buf.
Adjusts *bsize appropriately.
Returns the bits actually pushed. */
{
bits &= lowbits(nbits);
*buf |= bits << *bsize;
if ((*bsize += nbits) > sizeof *buf * CHAR_BIT) {
fputs("tasc: Bit buffer overflow\n", stderr);
exit(1);
}
return bits;
}
int asctobin(FILE *fasc, FILE *fbin)
/* Translate ASC to HP48 binary.
Returns 0 if ok, 1 on error. */
{
ulong buf=0, crc=0;
long nibs;
int bsize=0, c, d;
if (verb) fputs("Mode: ASC->bin\n", stderr);
/* scan input file to find line beginning with '"' */
c = '\n';
do {
d = c;
c = getc(fasc);
} while (c != EOF && (d != '\n' || c != '"'));
if (c == EOF) {
fputs("ASC->bin: Invalid ASC file\n", stderr);
return 1;
}
/* write header into binary file */
fputs("HPHP48-", fbin);
putc(rom, fbin);
if (verb)
fprintf(stderr, ROMFMT, rom);
/* translate data */
nibs = -4;
while (fscanf(fasc, "%1x", &d) == 1) {
pushbitq(&buf, &bsize, 4, d);
calc_crc(crc, d);
nibs++;
if (bsize >= 16 + CHAR_BIT)
fputc((int) popbitq(&buf, &bsize, CHAR_BIT), fbin);
}
/* check CRC */
if (bsize > 16)
fputc((int) popbitq(&buf, &bsize, bsize - 16), fbin);
if (crc || bsize != 16) {
fputs("ASC->bin: CRC failed\n", stderr);
return 1;
}
if (verb)
fprintf(stderr, BYTESFMT, buf, nibs/2, nibs&1?HALFFMT:"");
return 0;
}
int bintoasc(FILE *fbin, FILE *fasc)
/* Translates HP48 binary to ASC format.
Return 0 if ok, 1 on error. */
{
ulong buf=0, crc=0, skip=0;
long nibs;
int bsize=0, c, width=0;
enum { NONE, SIZE, ASCIC, ASCIX, DIR } state=NONE;
const int MAXWIDTH=64;
char str[7];
if (verb) fputs("Mode: bin->ASC\n", stderr);
/* check input for "HPHP48-" header */
if (fread(str, 1, 7, fbin) != 7
|| strncmp(str, "HPHP48-", 7)
|| (c = getc(fbin)) == EOF) {
fputs("bin->ASC: Invalid source file header\n", stderr);
return 1;
}
if (verb)
fprintf(stderr, ROMFMT, c);
/* write header into ASC file */
fprintf(fasc, "%%%%HP: T(1)A(R)F(.); @ tasc v%s file\n\"", VERSION);
nibs = 0;
while ((c = getc(fbin)) != EOF) {
pushbitq(&buf, &bsize, CHAR_BIT, c);
/* parse input HP objects */
if (!skip)
switch (state) {
case NONE: if (bsize >= 20) {
ulong pro = buf & lowbits(20);
skip = 5;
if (pro == 0x29e8uL || pro == 0x2a0auL || pro == 0x2a2cuL
|| pro == 0x2a4euL || pro == 0x2b1euL || pro == 0x2b40uL
|| pro == 0x2b62uL || pro == 0x2b88uL || pro == 0x2dccuL)
state = SIZE;
else if (pro == 0x2e48uL || pro == 0x2e6duL || pro == 0x2afcuL)
state = ASCIC;
else if (pro == 0x2a96uL) state = DIR, skip = 8;
else if (pro == 0x2911uL) skip = 10;
else if (pro == 0x2933uL) skip = 21;
else if (pro == 0x2955uL) skip = 26;
else if (pro == 0x2977uL) skip = 37;
else if (pro == 0x299duL) skip = 47;
else if (pro == 0x29bfuL) skip = 7;
else if (pro == 0x2e92uL) skip = 11;
}
break;
case SIZE: if (bsize >= 20)
state = NONE, skip = buf & lowbits(20);
break;
case ASCIC: if (bsize >= 8)
state = NONE, skip = 2 + 2 * (buf & lowbits(8));
break;
case ASCIX: if (bsize >= 8)
state = NONE, skip = 4 + 2 * (buf & lowbits(8));
break;
case DIR: if (bsize >= 20)
state = ASCIX, skip = buf & lowbits(20);
break;
}
/* write already interpreted binary data */
while (skip && bsize >= 4) {
c = (int) popbitq(&buf, &bsize, 4);
if (width == MAXWIDTH) {
putc('\n', fasc);
width = 0;
}
fprintf(fasc, "%1.1X", c);
width++;
calc_crc(crc, c);
skip--;
nibs++;
}
}
if (buf) {
fprintf(stderr, "bin->ASC: Binary parsed incorrectly\n");
return 1;
}
/* append CRC */
buf = crc;
bsize = 16;
while (bsize) {
if (width == MAXWIDTH) {
putc('\n', fasc);
width = 0;
}
fprintf(fasc, "%1.1X", (int) popbitq(&buf, &bsize, 4));
width++;
}
fputs("\"\n@ ", fasc);
fprintf(fasc, BYTESFMT, crc, nibs/2, nibs&1?HALFFMT:"");
if (verb)
fprintf(stderr, BYTESFMT, crc, nibs/2, nibs&1?HALFFMT:"");
return 0;
}
int translate(const char *fsrc, const char *fdest, int mode)
/* Translate file named fsrc to file named fdest, using mode mode.
fsrc == NULL means use stdin; fdest == NULL means use stdout.
Return 0 if ok, 1 on error. */
{
FILE *in, *out;
int i;
switch (mode) {
case ATOB: if (fsrc) {
in = fopen(fsrc, "r");
if (!in) { perror(fsrc); return 1; }
} else
in = stdin;
if (fdest) {
out = fopen(fdest, "wb");
if (!out) { perror(fdest); return 1; }
} else {
fputs("ASC->bin: Case not implemented\n",stderr);
exit(1);
}
i = asctobin(in, out);
break;
case BTOA: if (fsrc) {
in = fopen(fsrc, "rb");
if (!in) { perror(fsrc); return 1; }
} else {
fputs("bin->ASC: Case not implemented\n",stderr);
exit(1);
}
if (fdest) {
out = fopen(fdest, "w");
if (!out) { perror(fdest); return 1; }
} else
out = stdout;
i = bintoasc(in, out);
break;
default: fputs("tasc: Unimplemented translation mode\n", stderr);
exit(1);
}
fclose(in);
fclose(out);
return i;
}
int main(int argc, char **argv, char **envp)
/* Uses:
tasc options files
options:
-d Force ASC->bin translation (decode ASC)
-e Force bin->ASC translation (encode ASC)
-i Use stdin for -a mode / stdout for -b mode:
ignored in auto-translation
-q Suppress printouts (quiet)
-r<let> Set rom revision <let>:
ignored in bin->ASC translation
files:
names of input/output files
return code: 0 ok, 1 error */
{
int e, mode=0, stdio=0, argi;
char *p, *q;
/* interpret options as described above */
e = 0;
for (argi = 1; argv[argi] && argv[argi][0] == '-'; argi++)
for (p = argv[argi] + 1; *p; p++) {
switch (*p) {
case 'd': if (mode) e++; else mode = ATOB; break;
case 'e': if (mode) e++; else mode = BTOA; break;
case 'i': if (stdio) e++; else stdio = 1; break;
case 'q': if (verb) verb = 0; else e++; break;
case 'r': if (p[1]) {
p++;
if (!strchr("ABCDEF", rom = toupper(*p))) e++;
} else e++;
break;
default: e++;
}
}
if (e) {
fprintf(stderr, "Use: %s [-deiqr<let>] file [file]\n", argv[0]);
return 1;
}
if (verb) fprintf(stderr, "TASC version %s\nCompiled on %s at %s\n",
VERSION, __DATE__, __TIME__);
/* translate files by method specified */
switch (mode) {
case ATOB: if (stdio) {
if (argc - argi == 1) e = translate(0, argv[argi], ATOB);
else {
fputs("ASC->bin: Specify output filename\n", stderr);
return 1;
}
} else {
if (argc - argi == 2) e = translate(argv[argi], argv[argi+1], ATOB);
else {
fputs("ASC->bin: Specify input and output filenames\n", stderr);
return 1;
}
}
break;
case BTOA: if (stdio) {
if (argc - argi == 1) e = translate(argv[argi], 0, BTOA);
else {
fputs("bin->ASC: Specify input filename\n", stderr);
return 1;
}
} else {
if (argc - argi == 2) e = translate(argv[argi], argv[argi+1], BTOA);
else {
fputs("bin->ASC: Specify input and output filenames\n", stderr);
return 1;
}
}
break;
case 0: if (argc - argi == 1) {
int n;
n = strlen(argv[argi]);
q = alloc(n + sizeof ASCSUF);
strcpy(q, argv[argi]);
n -= sizeof ASCSUF - 1;
if (n > 0 && !stricmp(q+n, ASCSUF)) {
q[n] = 0;
e = translate(argv[argi], q, ATOB);
} else {
p = strrchr(q, ASCSUF[0]);
if (p) strcpy(p, ASCSUF); else strcat(q, ASCSUF);
e = translate(argv[argi], q, BTOA);
}
free(q);
} else if (argc - argi == 2) {
e = translate(argv[argi], argv[argi+1],
stricmp(argv[argi]+strlen(argv[argi])-(sizeof ASCSUF -1), ASCSUF)
? BTOA : ATOB);
} else {
fputs("tasc: Specify source [and target] filename[s]\n", stderr);
return 1;
}
break;
}
return e;
}